﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Noris.Schedule.Support.Services;
using Noris.Schedule.Support;

namespace Noris.Schedule.Planning.ProcessData
{
	#region VARIÁTOR PLÁNU: MRP zpětně, KPL dopředně
	/// <summary>
	/// Variátor plánovacího procesu:
	/// MRP zpětně, KPL dopředně
	/// </summary>
	public class PlanVariatorCplForward : IPlanVariator
    {
        #region IPlanVariationPlugin - DECLARATIONS
		/// <summary>
		/// Pořadí této varianty plánu v nabídce v toolbaru. Standardní varianty, dodávané LCS, mají hodnoty 100-199.
		/// </summary>
		public virtual int Order { get { return 100; } }
		/// <summary>
		/// Název této varianty plánu, který se zobrazuje v toolbaru v nabídce (combo box)
		/// </summary>
		public virtual string Name { get { return "MRP zpětně, KPL dopředně"; } }
		/// <summary>
		/// Rozšířený popis této varianty plánu, který se zobrazuje v toolbaru v bublině (ToolTip)
		/// </summary>
		public virtual string Description { get { return "Plánuje MRP zpětně, obsazování kapacit dopředné (od nejnižšího data)"; } }
        #endregion
		#region IPlanVariationPlugin - FUNCTIONS
		/// <summary>
		/// Metoda, která má za úkol setřídit seznam návrhů na výrobu do takového pořadí, ve kterém se budou poté registrovat do kapacitních úkolů.
		/// Seznam je předáván v args.AxisSPairList.
		/// Výchozí komparátory jsou k dispozici na třídě PlanningAxisSchedulePair jako statické metody CompareToFuture() / CompareToHistory().
		/// Je možno vytvořit i jiný komparátor. 
		/// Teoreticky je možné odebrat některé položky, pak nebudou naplánovány jejich kapacity.
		/// </summary>
		/// <param name="args">Data pro tuto metodu: seznam návrhů k setřídění a reference na plánovací proces</param>
		public virtual void PrepareAxisSListToScheduling(PlanVariatorAxisSListArgs args)
		{
			args.AxisSPairList.Sort(PlanningAxisSchedulePair.CompareToFuture);
		}
		#endregion
		#region IDisposable Members
        void IDisposable.Dispose()
        {
        }
        #endregion
    }
	#endregion
	#region VARIÁTOR PLÁNU: MRP zpětně, KPL zpětně
	/// <summary>
	/// Variátor plánovacího procesu:
	/// MRP zpětně, KPL zpětně
	/// </summary>
	public class PlanVariatorCplBackward : IPlanVariator
	{
		#region IPlanVariationPlugin - DECLARATIONS
		/// <summary>
		/// Pořadí této varianty plánu v nabídce v toolbaru. Standardní varianty, dodávané LCS, mají hodnoty 100-199.
		/// </summary>
		public virtual int Order { get { return 101; } }
		/// <summary>
		/// Název této varianty plánu, který se zobrazuje v toolbaru v nabídce (combo box)
		/// </summary>
		public virtual string Name { get { return "MRP zpětně, KPL zpětně"; } }
		/// <summary>
		/// Rozšířený popis této varianty plánu, který se zobrazuje v toolbaru v bublině (ToolTip)
		/// </summary>
		public virtual string Description { get { return "Plánuje MRP zpětně, obsazování kapacit zpětně (od nejvyššího data)"; } }
        #endregion
		#region IPlanVariationPlugin - FUNCTIONS
		/// <summary>
		/// Metoda, která má za úkol setřídit seznam návrhů na výrobu do takového pořadí, ve kterém se budou poté registrovat do kapacitních úkolů.
		/// Seznam je předáván v args.AxisSPairList.
		/// Výchozí komparátory jsou k dispozici na třídě PlanningAxisSchedulePair jako statické metody CompareToFuture() / CompareToHistory().
		/// Je možno vytvořit i jiný komparátor. 
		/// Teoreticky je možné odebrat některé položky, pak nebudou naplánovány jejich kapacity.
		/// </summary>
		/// <param name="args">Data pro tuto metodu: seznam návrhů k setřídění a reference na plánovací proces</param>
		public virtual void PrepareAxisSListToScheduling(PlanVariatorAxisSListArgs args)
		{
			args.AxisSPairList.Sort(PlanningAxisSchedulePair.CompareToHistory);
		}
		#endregion
		#region IDisposable Members
		void IDisposable.Dispose()
        {
        }
        #endregion
    }
	#endregion
    #region CLASS PlanVariationManager : správce variant plánovacího procesu
    internal class PlanVariatorManager
    {
        #region SINGLETON, KONSTRUKCE
        private static PlanVariatorManager Current
        {
            get
            {
                if (_Current == null)
                {
                    lock (_Locker)
                    {
                        if (_Current == null)
                        {
                            _Current = new PlanVariatorManager();
                        }
                    }
                }
                return _Current;
            }
        }
        private PlanVariatorManager()
        {
            // Získám přehled všech tříd, které představují varianty plánovacího procesu:
            IEnumerable<IPlanVariator> services = Steward.GetServiceObjectList<IPlanVariator>(true);

            // Přidám k tomu unikátní čísla VariationID a uložím:
            this._VariationPlugins = new List<KeyValuePair<VariationID, IPlanVariator>>();
            foreach (IPlanVariator service in services)
                this._VariationPlugins.Add(new KeyValuePair<VariationID, IPlanVariator>(new VariationID(this._VariationPlugins.Count), service));

            // Setřídit podle Order:
            if (this._VariationPlugins.Count > 1)
                this._VariationPlugins.Sort(delegate(KeyValuePair<VariationID, IPlanVariator> a, KeyValuePair<VariationID, IPlanVariator> b) { return a.Value.Order.CompareTo(b.Value.Order); });


            this._PlanVariationId = null;
        }
        private static PlanVariatorManager _Current = null;
        private static object _Locker = new object();
        private List<KeyValuePair<VariationID, IPlanVariator>> _VariationPlugins;
        private VariationID _PlanVariationId;
        #endregion
        #region GET LIST, SET CURRENT
        /// <summary>
        /// Vrátí soupis definičních dat položek ComboBoxu pro nabídku variant procesu.
        /// Vrácené položky se pak nabídnou v toolbaru.
        /// </summary>
        /// <param name="creator"></param>
        /// <returns></returns>
        internal static List<FunctionGlobalItem> GetVariationItems(object creator)
        {
            // Current obsahuje načtený a zpracovaný seznam variant plánu (objekty implementující IPlanVariationPlugin)
            PlanVariatorManager current = Current;

            // Vytvořím seznam definic ComboItem:
            List<FunctionGlobalItem> varItems = new List<FunctionGlobalItem>();
            foreach (KeyValuePair<VariationID, IPlanVariator> varKeyKvp in current._VariationPlugins)
            {
                IPlanVariator variation = varKeyKvp.Value;
                FunctionGlobalItem varItem = FunctionGlobalItem.CreateComboItem(creator, varKeyKvp.Key, null, variation.Name, variation.Description);
                if (varItem != null)
                    varItems.Add(varItem);
            }
            return varItems;
        }
        /// <summary>
        /// Najde a nastaví pro příští běh defaultní variantu procesu.
        /// </summary>
        internal static VariationID PrepareDefaultVariationId()
        {
            // Nový seznam variátorů, budeme jej třídit dle Order:
            List<KeyValuePair<VariationID, IPlanVariator>> variators = new List<KeyValuePair<VariationID,IPlanVariator>>(Current._VariationPlugins);
            if (variators.Count == 0) return null;

            // Takhle najdu do položky [0] variátor s nejnižším Order:
            variators.Sort((a, b) => a.Value.Order.CompareTo(b.Value.Order));

            // Hodnotu VariationID z variátoru s nejnižším Order si uložím jako vybranou variantu:
            VariationID vId = variators[0].Key;
            Current._PlanVariationId = vId;
            return vId;
        }
        /// <summary>
        /// Najde a nastaví pro příští běh specifickou variantu procesu.
        /// </summary>
        /// <param name="planVariation">Číslo varianty plánu. Pokud je null, nebudu měnit (měl by být zvolen defaultní)</param>
        internal static VariationID PrepareSpecificVariationId(Int32? planVariation)
        {
            if (!planVariation.HasValue) return null;
            int order = planVariation.Value;

            List<KeyValuePair<VariationID, IPlanVariator>> variators = Current._VariationPlugins;
            if (variators == null || variators.Count == 0)
            {
                Steward.Audit(AuditRowType.ApplicationError, MessageInfo.Get("Pro danou kapacitní verzi plánu je požadována varianta %0, v plánovací tabuli ale neexistují žádné varianty.", order));
                return null;
            }

            List<KeyValuePair<VariationID, IPlanVariator>> specifics = variators.FindAll(kvp => kvp.Value.Order == order);
            if (specifics == null || specifics.Count == 0)
            {
                Steward.Audit(AuditRowType.ApplicationError, MessageInfo.Get("Pro danou kapacitní verzi plánu je požadována varianta %0, ta ale v plánovací tabuli neexistuje.", order));
                return null;
            }

            if (specifics.Count > 1)
            {
                string types = "";
                foreach (var kvp in specifics)
                {
                    IPlanVariator variator = kvp.Value;
                    Type varType = variator.GetType();
                    types += (types.Length == 0 ? "" : "; ") + variator.Name + " (" + varType.Namespace + "." + varType.Name + ")";
                }
                Steward.Audit(AuditRowType.ApplicationError, MessageInfo.Get("Pro danou kapacitní verzi plánu je požadována varianta %0, pro tuto variantu existuje více pluginů (se shodnou hodnotou Order): %1.", order));
            }

            // Máme 1 a více variátorů, aktivujeme první z nich:
            VariationID vId = specifics[0].Key;
            Current._PlanVariationId = vId;
            return vId;
        }
        /// <summary>
        /// Nastaví do managera hodnotu ID varianty plánu, která se má příště použít.
        /// </summary>
        /// <param name="variationID"></param>
        internal static void SetCurrentVariation(VariationID variationID)
        {
            Current._PlanVariationId = variationID;
        }
        /// <summary>
        /// Vytvoří nový objekt typu IPlanVariationPlugin pro aktuálně vybranou variantu plánu, a vrátí ji.
        /// </summary>
        /// <returns></returns>
        internal static IPlanVariator CreatePlanVariator(bool enableDefaultVariator)
        {
			IPlanVariator variator = null;
			
			VariationID variationID = Current._PlanVariationId;
            if (variationID == null && enableDefaultVariator)
                // Pokud nemám definovánu variantu explicitně, zkusím najít defaultní:
                variationID = PrepareDefaultVariationId();
            if (variationID == null)
				Throw.SysError(MessageInfo.Get("Pro spouštění plánovacího procesu nebyla připravena varianta procesu."));

			KeyValuePair<VariationID, IPlanVariator> varKvp = Current._VariationPlugins.Find(i => (VariationID.IsEqual(i.Key, variationID)));
			if (varKvp.Key == null)
				Throw.SysError(MessageInfo.Get("Zvolená varianta plánovacího procesu (%0) nebyla nalezena.", variationID));

			variator = Steward.GetServiceObject<IPlanVariator>(varKvp.Value.GetType(), false);
			return variator;
        }
        #endregion
    }
    #region CLASS VariationID
    /// <summary>
    /// Interní třída, která představuje klíč varianty.
    /// Uvnitř je sice jednoduchý Int32, ale navenek jde o třídu VariationID, která už tímto type, dává najevo, že jde o klíč varianty.
    /// Podle tohoto typu je detekován výkon funkce v FunctionGlobal :
    ///   v metodě RunToolItem se detekuje typ údaje FunctionID, a pokud se jedná o typ VariationID, pak je zřejmé, že jde o funkci "Nastavit variantu procesu".
    /// </summary>
    internal class VariationID : IComparable
    {
        internal VariationID(int id)
        {
            this._Id = id;
        }
        private int _Id;
        public override string ToString()
        {
            return this._Id.ToString();
        }
        #region IComparable Members
        int IComparable.CompareTo(object obj)
        {
            if (obj is VariationID)
                return this._Id.CompareTo(((VariationID)obj)._Id);
            return 1;
        }
		public static bool IsEqual(VariationID a, VariationID b)
		{
			if (a == null || b == null) return false;
			return (a._Id == b._Id);
		}
        #endregion
    }
    #endregion
    #endregion
}
